#include <iostream>
#include <fstream>
#include <string>

#include "errors.h"
#include "tokenizer.h"
#include "basicparser.h"
#include "basicparser2.h"
#include "parser.h"
#include "codegenerator.h"
#include "cmd.h"

using namespace std;

/*
    If current profile is Debug then the following array will be passed as options
*/
#ifdef _DEBUG

// "--tokenizer-only"
// "--expressions-parser-only"
// "--expressions-parser2-only"
// "--parser-only"
// "--ParserWithSymTable-only"
char *options[] =
{
     "--generator-only",
 //"C:\\Users\\Kravitz\\Documents\\Visual Studio 2008\\Projects\\ccompiler\\tests\\tests\\ParserWithSymTable\\08.tst",
    "tests\\tests\\generator\\50.c"
    //"D:\\work\\ccompiler\\tests\\tests\\ParserWithSymTable\\04.tst"
};
#endif

void initialize()
{
    __InitializeTokenizer::initialize();
    __InitializeBasicParser::initialize();
    __InitializeBasicParser2::initialize();
    __InitializeParserWithSymTable::initialize();
    __InitializeCodegenerator::initialize();
}

int main(int argc, char *argv[])
{
    initialize();
    string filename;
    char **args = argv;
#ifdef _DEBUG
    {
        argc = (sizeof(options) / sizeof(char *) + 1);
        args = new char *[argc];
        args[0] = argv[0];
        for(int i = 1; i < argc; ++i)
        {
            args[i] = options[i-1];
        }
    }
#endif
    try
    {
        cmd::parseCmd(argc, args);
    }
    catch(CCompiler::Errors::CmdOptionsError)
    {
        cout << "Wrong command string" << endl;
    }
    if(cmd::needToPrintHelp())
    {
        cout << "CCompiler by Kravtsov Dmitriy" << endl;
        cout << "Implemented components:" << "tokenizer, expressions parser, symbols table" << endl;
        cout << "To use type: ccompiler.exe [options] <filename>" << endl;
        cout << "Available options are:" << endl;
        cout << "\t--tokenizer-only - use only tokenizer" << endl;
        cout << "\t--expressions-parser-only - use only expressions parser" << endl;
        cout << "\t--expressions-parser2-only - use only expressions parser, second version" << endl;
        cout << "\t--ParserWithSymTable-only - use only expressions parser, with full\n" <<
                 "\t\t\t\t    version of symbols table" << endl;
        cout << "\t--parser-only - use only full parser" << endl;
		cout << "\t--generator-only - use only full parser" << endl;
    }
    else
    {
        filename = cmd::getFilename();
        try
        {
            ifstream file(filename.c_str());
            CCompiler::Tokenizer tokenizer(file);
            if(cmd::lexerOptionEnabled())
            {
                CCompiler::Token t = tokenizer.getCurToken();
                while(t.getTokenType() != CCompiler::ttEof)
                {
                    cout << t;
                    t = tokenizer.nextToken();
                }
            }
            else if(cmd::parseExpressionsOptionEnabled())
            {
                CCompiler::BasicParser parser(tokenizer);
                while(tokenizer.getCurToken().getTokenType() != CCompiler::ttEof)
                {
                    CCompiler::SynNode *expr = parser.parseExpression();
                    expr->print(std::cout);
                    delete expr;
                }
            }
            else if(cmd::parseExpressions2OptionEnabled())
            {
                CCompiler::BasicParser2 parser(tokenizer);
                while(tokenizer.getCurToken().getTokenType() != CCompiler::ttEof)
                {
                    CCompiler::SynNode *expr = parser.parseExpression2();
                    expr->print(std::cout);
                    delete expr;
                }
            }
            else if(cmd::parserWithSymTableOptionEnabled())
            {
                CCompiler::ParserWithSymTable parser(tokenizer);
                try
                {
                    while(tokenizer.getCurToken().getTokenType() != CCompiler::ttEof)
                    {
                        CCompiler::SynNode *expr = parser.oldparse();
                        expr->print(std::cout);
                        delete expr;
                    }
                }
                catch(.../*CCompiler::Errors::EndOfFile*/)
                {
                    parser.print(cout << "::Global table\n");
                    cout << "::End of global table\n";
                    throw;
                }
                parser.print(cout << "::Global table\n");
                cout << "::End of global table\n";
            }
            else if(cmd::parseOptionEnabled())
            {
                CCompiler::ParserWithSymTable parser(tokenizer);
                try
                {
                    parser.parse();
                }
                catch(...)
                {
                    parser.print(cout << "::Global table\n");
                    cout << "::End of global table\n";
                    throw;
                }
                parser.print(cout << "::Global table\n");
                cout << "::End of global table\n";
            }
			else if(cmd::generatorOptionEnabled())
			{
				CCompiler::ParserWithSymTable parser(tokenizer);
				try
                {
                    parser.parse();
                }
                catch(CCompiler::Errors::EndOfFile) {}
                CCompiler::Generator generator(cmd::getOutFilename(), parser.accessSymTable());
                generator.generate();
			}
        }
        catch(CCompiler::Errors::BrokenStream)
        {
            cerr << "Unable to read from file " << filename;
        }
        catch(CCompiler::Errors::BaseErrorWithMsg &e)
        {
            cerr << e;
        }
        catch(CCompiler::Errors::EndOfFile)
        {}
    }
#ifdef _DEBUG
    delete[] args;
#endif
}
